package online.inote.common.utils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

/**
 * <p>
 * Desc: 
 * </p>
 *
 * @author XQF SUI
 * @created 2018年1月31日 上午9:42:12
 * @version 1.0
 */
public class ClassUtils extends org.springframework.util.ClassUtils {

	private static final String RESOURCE_PATTERN = "/**/*.class";
	private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
	
	/**
	 * <p>
	 * Desc: 根据多过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午9:50:07
	 * @param filter 过滤器
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClassByMultiFilter(TypeFilter[] filters, String... packagePath) {
		
		if (packagePath == null || packagePath.length < 1) {
			return null;
		}
		
		Set<Class<?>> classSet = new HashSet<>();
		
		try {
			for (String path : packagePath) {
				String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + convertClassNameToResourcePath(path.trim()) + RESOURCE_PATTERN;
				Resource[] resources = resourcePatternResolver.getResources(pattern);
				MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
				
				for (Resource resource : resources) {
			        if (resource.isReadable()) {
			            MetadataReader reader = readerFactory.getMetadataReader(resource);
			            
			            if (filters != null && filters.length > 0) {
			            	boolean b = true;
			            	
			            	for (TypeFilter filter : filters) {
			            		if (!filter.match(reader, readerFactory)) {
					            	b = false;
					            	break;
					            }
							}
			            	
			            	if (b) {
			            		classSet.add(Class.forName(reader.getClassMetadata().getClassName()));
			            	}
			            } else {
			            	classSet.add(Class.forName(reader.getClassMetadata().getClassName()));
			            }
			        }
			    }
			}
		} catch (ClassNotFoundException | IOException e) {
			throw new RuntimeException(e);
		}
		
		return classSet;
	}
	
	/**
	 * <p>
	 * Desc: 根据单过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:06:29
	 * @param filter 过滤器
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClassByFilter(TypeFilter filter, String... packagePath) {
		
		if (filter == null) {
			return scanClassByMultiFilter(null, packagePath);
		}
		
		TypeFilter[] filters = {filter};
		
		return scanClassByMultiFilter(filters, packagePath);
	}
	
	/**
	 * <p>
	 * Desc: 扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午9:52:47
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClass(String... packagePath) {
		return scanClassByMultiFilter(null, packagePath);
	}
	
	/**
	 * <p>
	 * Desc: 根据多注解过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:19:38
	 * @param annotations
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassByAnnotation(Class<? extends Annotation>[] annotations, String... packagePath) {
		
		if (annotations == null || annotations.length < 1) {
			scanClassByMultiFilter(null, packagePath);
		}
		
		int i = 0;
		TypeFilter[] filters = new TypeFilter[annotations.length];
		
		for (Class<? extends Annotation> annotation : annotations) {
			AnnotationTypeFilter filter = new AnnotationTypeFilter(annotation, false);
			filters[i++] = filter;
		}
		
		return scanClassByMultiFilter(filters, packagePath);
	}
	
	/**
	 * <p>
	 * Desc: 根据单注解过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:21:00
	 * @param annotation
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassByAnnotation(Class<? extends Annotation> annotation, String... packagePath) {
		
		if (annotation == null) {
			scanClassByMultiFilter(null, packagePath);
		}
		
		TypeFilter[] filters = {new AnnotationTypeFilter(annotation, false)};
		
		return scanClassByMultiFilter(filters, packagePath);
	}
	
	/**
	 * <p>
	 * Desc: 根据父类扫描class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:22:51
	 * @param clazz
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassBySuperClass(Class<?> clazz, String... packagePath) {
		TypeFilter[] filters = {new AssignableTypeFilter(clazz)};
		return scanClassByMultiFilter(filters, packagePath);
	}
	
	/**
	  * <p>
	  * Desc: 获取接口的泛型
	  * </p>
	  *
	  * @author Sui
	  * @created 2017年12月10日 下午11:00:23
	  * @param clazz
	  * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getGenericInterface(Class<?> clazz) {
		Type genType = clazz.getGenericInterfaces()[0];
      Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
      return (Class<T>) params[0];
	}
	
	/**
	  * <p>
	  * Desc: 获取类的泛型
	  * </p>
	  *
	  * @author Sui
	  * @created 2017年12月10日 下午11:08:14
	  * @param clazz
	  * @return
	 */
	@SuppressWarnings("unchecked")
   public static <T> Class<T> getGenericClass(Class<?> clazz) {
       Type genType = clazz.getGenericSuperclass();
       Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
       return (Class<T>) params[0];
   }
}